FeignClient 读取类上的公用RequestMapping

代码先行

SpringCloud 中使用Feign远程调用。

Provider
@RequestMapping(value = "/common-url")
@ResponseBody
public class TestController {
    @GetMapping(value = "/hello")
    String hello(Stirng param){
		return "hello from provider :" + param;
	}
}
API接口
@FeignClient(value = "spring-cloud-producer")
@RequestMapping(value = "/common-url")
@ResponseBody
public interface HelloRemote {
    @GetMapping(value = "/hello")
    String hello(Stirng param);
}

Consumer

@RestController
public class ConsumerController {

    @Autowired
    HelloRemote helloRemote;

    @RequestMapping("/hello/{name}")
    public String index(@PathVariable("name") String name) {
        return helloRemote.hello(name);
    }

}

上面是一个简单的调用,访问consumer的hello/zhangsan会打印hello from provider : zhangsan

但是假如API中的testService变成了public interface HelloRemote extends TestInterface 实际调用会出现问题,调用consumer会显示:type=Internal Server Error, status=500

查看控制台发现:{"timestamp":1564244028793,"status":404,"error":"Not Found","message":"No message available","path":"/hello"}

直接报404调试发现请求的实际URL是--spring-cloud-producer/hello?name=zhangsan

image.png

为什么会这样呢?

一路追踪发现,默认解析是在org.springframework.cloud.openfeign.support.SpringMvcContract

protected void processAnnotationOnClass(MethodMetadata data, Class<?> clz) {
		//问题就在这个判断 导致没有读取公用的RequestMapping
		if (clz.getInterfaces().length == 0) {
			RequestMapping classAnnotation = findMergedAnnotation(clz,
					RequestMapping.class);
			if (classAnnotation != null) {
				// Prepend path from class annotation if specified
				if (classAnnotation.value().length > 0) {
					String pathValue = emptyToNull(classAnnotation.value()[0]);
					pathValue = resolve(pathValue);
					if (!pathValue.startsWith("/")) {
						pathValue = "/" + pathValue;
					}
					data.template().insert(0, pathValue);
				}
			}
		}
	}
	

这里的HelloRemote由于interface数>0所以直接忽略掉了@RequestMapping(value = "/common-url")

解决方法:自己实现一个Contract
public class CustomContract extends SpringMvcContract {

    private ResourceLoader resourceLoader = new DefaultResourceLoader();


    public CustomContract(List<AnnotatedParameterProcessor> annotatedParameterProcessors) {
        super(annotatedParameterProcessors);
    }

    public CustomContract(List<AnnotatedParameterProcessor> annotatedParameterProcessors, ConversionService
            conversionService) {
        super(annotatedParameterProcessors, conversionService);
    }

    @Override
    protected void processAnnotationOnClass(MethodMetadata data, Class<?> clz) {
        //总是读取 client 的前缀
        RequestMapping classAnnotation = findMergedAnnotation(clz,
                RequestMapping.class);
        if (classAnnotation != null) {

            if (classAnnotation.value().length > 0) {
                String pathValue = emptyToNull(classAnnotation.value()[0]);
                pathValue = resolve(pathValue);
                if (!pathValue.startsWith("/")) {
                    pathValue = "/" + pathValue;
                }
                data.template().insert(0, pathValue);
            }
        }
    }

    private String resolve(String value) {
        if (StringUtils.hasText(value)
                && this.resourceLoader instanceof ConfigurableApplicationContext) {
            return ((ConfigurableApplicationContext) this.resourceLoader).getEnvironment()
                    .resolvePlaceholders(value);
        }
        return value;
    }
}

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×